#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
#include <iostream>
#include <fstream>
#include <sys/select.h>
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <error.h>

class server
{
public:
    server(short port)
        : _port(port), _pee(new epoll_event[64])
    {
        _listenfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listenfd == -1)
        {
            std::cout << "socket error!" << std::endl;
            exit(-1);
        }
        int val = 1;
        setsockopt(_listenfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));

        sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(_port);
        server.sin_addr.s_addr = INADDR_ANY;
        int n = bind(_listenfd, (const sockaddr *)&server, sizeof(server));
        if (n == -1)
        {
            std::cout << "bind error!" << std::endl;
            exit(-1);
        }

        n = listen(_listenfd, 5);
        if (n == -1)
        {
            std::cout << "listen error!" << std::endl;
            exit(-1);
        }
        setnoblocking(_listenfd);
        _epfd = epoll_create(5); // 创建epoll例程
        epoll_event event;
        event.data.fd = _listenfd;
        event.events = EPOLLIN;
        epoll_ctl(_epfd, EPOLL_CTL_ADD, _listenfd, &event);
    }

    void setnoblocking(int fd)
    {
        int tmp = fcntl(fd, F_GETFL, 0);      // 获取描述符当前状态
        fcntl(fd, F_SETFL, tmp | O_NONBLOCK); // 修改状态
    }

    void start()
    {
        while (true)
        {
            int ret = epoll_wait(_epfd, _pee, 64, -1);
            if (ret == 0)
            {
                printf("Time Out!\n");
                continue;
            }
            if (ret == -1)
            {
                printf("select error!\n");
                break;
            }
            printf("epoll_wait唤醒!\n");
            // 监视的文件描述符有事件发生了
            for (int i = 0; i < ret; i++)
            {
                if (_pee[i].data.fd == _listenfd)
                {
                    sockaddr_in client;
                    socklen_t len = sizeof(client);
                    int connfd = accept(_pee[i].data.fd, (sockaddr *)&client, &len);
                    setnoblocking(connfd);
                    epoll_event event;
                    event.data.fd = connfd;
                    event.events = EPOLLIN | EPOLLET;
                    epoll_ctl(_epfd, EPOLL_CTL_ADD, connfd, &event);
                    printf("新的客户端连接！%d\n", connfd);
                }
                else
                {
                    while (true)
                    {
                        char buffer[5] = {0};
                        int n = read(_pee[i].data.fd, buffer, sizeof(buffer) - 1);
                        if (n > 0)
                        {
                            write(_pee[i].data.fd, buffer, n);
                        }
                        else if (n == 0)
                        {
                            epoll_ctl(_epfd, EPOLL_CTL_DEL, _pee[i].data.fd, nullptr);
                            close(_pee[i].data.fd);
                            printf("客户端下线！%d\n", _pee[i].data.fd);
                            break;
                        }
                        else 
                        {
                            if(errno == EAGAIN) break;
                        }
                    }
                }
            }
        }
        close(_listenfd);
    }

protected:
    int _listenfd;
    short _port;
    epoll_event *_pee;
    int _epfd;
};

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        printf("Usage: %s <port>\n", argv[0]);
        return -1;
    }
    short port = atoi(argv[1]);
    server s(port);
    s.start();
    return 0;
}
